/*
 * @Author: 李石
 * @Date: 2024-05-20 16:21:47
 * @LastEditors: lishi
 * @LastEditTime: 2024-06-28 10:40:20
 * @Description: 
 * Copyright (c) 2024 by ${lishi0105@163.com}, All Rights Reserved. 
 */
#ifndef _FRAME_PULLER_HPP_
#define _FRAME_PULLER_HPP_

#include <atomic>
#include <mutex>
#include <cmath>
#include <stdint.h>
#include <functional>
#include "time_util.h"
// #include "logger.hpp"
#include "FrameData.hpp"
#include "BaseShared.hpp"

typedef std::function<void(const FrameData::Ptr)> pullerDataCallback;
class FramePuller : public SharedBase<FramePuller> {
public:
    FramePuller(const std::string &url, int rtp_type, pullerDataCallback cb_):
        m_url(url), m_rtp_type(rtp_type), 
        m_first_frame_ts(0), m_recv_counter(0),
        m_frame_fps(0),m_alive(false), 
        m_width(0), m_height(0),
        m_codec_id(-1), m_data_callback(cb_)
    {
        if(m_url.empty()){
            std::runtime_error("url is empty");
        }
        if(m_rtp_type != 0 && m_rtp_type != 1){
            std::runtime_error("rtp_type is invalid");
        }
    }
    ~FramePuller(){
    };
    FramePuller(const FramePuller&) = delete;
    FramePuller& operator=(const FramePuller&) = delete;
    int codec_id() { return m_codec_id.load(); }
    virtual int fps() const { return m_frame_fps.load(); }
    int fact_fps() const {
        std::lock_guard<std::mutex> lock(m_fps_mutex);
        if(m_recv_counter < 25){return 0;}
        std::time_t m_current_ts = TimeUtil::GetSecondTimestamp();
        auto differ = m_current_ts - m_first_frame_ts;
        if(differ < 1){return 0;}
        double fps = (m_recv_counter*1.0) / (differ*1.0);
        std::cout<< "[FramePuller.hpp:"<<__LINE__<<"] fps: " << fps <<" m_recv_counter "<<m_recv_counter<< std::endl;
        return std::round(fps);
    }
    virtual int width() const { return m_width.load(); }
    virtual int height() const { return m_height.load(); }
    virtual bool alive() const { return m_alive.load(); }
    void onVideoAttribute(int32_t codec_id, float fps, int32_t width, int32_t height)
    {
        m_frame_fps.store(fps);
        m_width.store(width);
        m_height.store(height);
        m_codec_id.store(codec_id);
    }
    void resetPullerCounter(){
        std::lock_guard<std::mutex> lock(m_fps_mutex);
        m_frame_fps.store(0);
        m_width.store(0);
        m_height.store(0);
        m_codec_id.store(-1);
        m_alive.store(false);
        m_recv_counter = 0;
    }
protected:
    void onGetFrameData(const FrameData::Ptr frame){
        if(frame == nullptr)return;
        if(frame->dropAble())return;
        m_alive.store(true);
        {
            std::lock_guard<std::mutex> lock_fps(m_fps_mutex);
            m_recv_counter ++;
            if(m_recv_counter > 65535)m_recv_counter = 1;
            if(m_recv_counter == 1){m_first_frame_ts = TimeUtil::GetSecondTimestamp();}
        }
        std::lock_guard<std::mutex> lock(m_mutex);
        if(m_data_callback){
            m_data_callback(frame);
        }
    }

protected:
    std::string                  m_url;
    int                          m_rtp_type;
    mutable std::mutex           m_fps_mutex;
    std::mutex                   m_mutex;
    std::time_t                  m_first_frame_ts;
    std::atomic<int>             m_recv_counter;
    std::atomic<int>             m_frame_fps;
    std::atomic<bool>            m_alive;
    std::atomic<int32_t>         m_width;
    std::atomic<int32_t>         m_height;
    std::atomic<int32_t>         m_codec_id;
    pullerDataCallback           m_data_callback;
};

#endif // _FRAME_PULLER_HPP_